Medium 清新閱讀版
:連結
前兩天,我們探討了「網站文章」的情境題;今明兩天,就讓我們探討另一個情境題「會員註冊」吧!
這邊我們同樣假設網站是採前後端分離的設計,因此我們就專注在測試 API 的部分,不過會多一個「註冊驗證信」的部分要實作與做測試驗證。
使用者可填寫註冊資料後送出資料。
使用者可收到註冊驗證信,且該信件內含有專屬於該使用者的驗證連結。
使用者點選註冊驗證信中的驗證連結後,將驗證成功,其帳號驗證狀態將轉為已驗證。
這邊我們使用驗證時間來取代驗證狀態,並以有無驗證時間來判斷是否已驗證
依據以上的使用案例,我們可規畫出以下 API / 功能:
POST /registers
GET /users/{id}/validation?token={token}
接著就來實作 API 與註冊驗證信的邏輯吧!
database/migrations/2014_10_12_000000_create_users_table.php
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('users', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->string('email')->unique();
$table->timestamp('email_verified_at')->nullable();
$table->string('password');
$table->string('verify_email_token', 128)->nullable();
$table->rememberToken();
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('users');
}
};
app/Models/User.php
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Laravel\Sanctum\HasApiTokens;
class User extends Authenticatable
{
use HasApiTokens, HasFactory, Notifiable;
/**
* The attributes that are mass assignable.
*
* @var array<int, string>
*/
protected $fillable = [
'name',
'email',
'password',
'email_verified_at',
'verify_email_token',
];
/**
* The attributes that should be hidden for serialization.
*
* @var array<int, string>
*/
protected $hidden = [
'password',
'remember_token',
];
/**
* The attributes that should be cast.
*
* @var array<string, string>
*/
protected $casts = [
'email_verified_at' => 'datetime',
];
}
routes/web.php
<?php
use App\Http\Controllers\UserController;
use Illuminate\Support\Facades\Route;
Route::post('/register', [UserController::class, 'register'])
->name('register');
Route::get('/verify-user-email', [UserController::class, 'verifyUserEmail'])
->name('verify-user-email');
app/Http/Controllers/UserController.php
<?php
namespace App\Http\Controllers;
use App\Mail\VerifyUserMail;
use App\Models\User;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Mail;
use Illuminate\Support\Str;
class UserController extends Controller
{
public function register(Request $request)
{
$request->validate([
'name' => 'required',
'email' => 'required',
'password' => 'required',
]);
$user = User::create([
'name' => $request->input('name'),
'email' => $request->input('email'),
'password' => Hash::make($request->input('password')),
'verify_email_token' => Str::random(128),
]);
Mail::to($user->email)
->send(new VerifyUserMail($user));
return response()->json('');
}
public function verifyUserEmail(Request $request)
{
$request->validate([
'token' => 'required',
]);
$token = $request->input('token');
$user = User::where('verify_email_token', $token)->first();
if (empty($user)) {
return response('Failed', 404);
}
$user->email_verified_at = now();
$user->save();
return response('Success');
}
}
app/Mail/VerifyUserMail.php
<?php
namespace App\Mail;
use App\Models\User;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Mail\Mailable;
use Illuminate\Queue\SerializesModels;
class VerifyUserMail extends Mailable
{
use Queueable, SerializesModels;
private $user;
/**
* Create a new message instance.
*
* @return void
*/
public function __construct(User $user)
{
$this->user = $user;
}
/**
* Build the message.
*
* @return $this
*/
public function build()
{
$data = [
'verifyLink' => route('verify-user-email', ['token' => $this->user->verify_email_token]),
];
return $this->with($data)
->view('view.mail.verify-email');
}
}
resources/views/mail/verify-email.blade.php
<!DOCTYPE html>
<html >
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Verify Mail</title>
</head>
<body class="antialiased">
<a href="{{ $verifyLink }}">Verify Your Email</a>
</body>
</html>
這邊我們要準備的是User 的 Factory 類別:
User Factory
<?php
namespace Database\Factories;
use Illuminate\Support\Str;
use Illuminate\Database\Eloquent\Factories\Factory;
class UserFactory extends Factory
{
/**
* Define the model's default state.
*
* @return array
*/
public function definition(): array
{
return [
'name' => $this->faker->name,
'email' => $this->faker->safeEmail,
'password' => bcrypt($this->faker->password),
'remember_token' => Str::random(10),
'verify_email_token' => Str::random(128),
];
}
}
到這邊為止,我們已經把測試目標準備好了,明天我們就來針對各使用案例來寫測試吧!